﻿using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using ZFY.Core;
using ZFY.DDD.Domain;

namespace ZFY.UMC.Model
{
    public abstract class ZFYDbContext<TDbContext> : DbContext where TDbContext : DbContext
    {
        protected ZFYDbContext(DbContextOptions<TDbContext> options)
            : base(options)
        {

        }
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            var entityTypeList = modelBuilder.Model.GetEntityTypes().Where(x => typeof(IAudit).IsAssignableFrom(x.ClrType));
            foreach (var entityType in entityTypeList)
            {
                var parameter = Expression.Parameter(entityType.ClrType);

                var propertyMethodInfo = typeof(EF).GetMethod("Property").MakeGenericMethod(typeof(bool));

                var isDeletedProperty = Expression.Call(propertyMethodInfo, parameter, Expression.Constant("IsDeleted"));

                BinaryExpression compareExpression = Expression.MakeBinary(ExpressionType.Equal, isDeletedProperty, Expression.Constant(false));

                var lambdaExpression = Expression.Lambda(compareExpression, parameter);

                modelBuilder.Entity(entityType.ClrType).HasQueryFilter(lambdaExpression);

            }
            base.OnModelCreating(modelBuilder);
        }
        public override async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default)
        {
            try
            {
                SetAudit();

                var res= await base.SaveChangesAsync(acceptAllChangesOnSuccess,cancellationToken);

                return res;
            }
            catch (DbUpdateConcurrencyException ex)
            {
                throw new DbUpdateConcurrencyException(ex.Message, ex);
            }
            finally
            {
                ChangeTracker.AutoDetectChangesEnabled = true;
            }
        }

        private void SetAudit()
        {
            foreach (var entry in ChangeTracker.Entries().ToList())
            {
                switch (entry.State)
                {
                    case EntityState.Added:
                        if (entry.Entity is IAudit)
                        {
                            ((IAudit)entry.Entity).CreationTime = DateTime.Now;
                        }
                        break;
                    case EntityState.Modified:
                        if (entry.Entity is IAudit)
                        {
                            ((IAudit)entry.Entity).LastModificationTime = DateTime.Now;
                        }
                        break;
                    case EntityState.Deleted:
                        if (entry.Entity is IAudit)
                        {
                            var entity = (IAudit)entry.Entity;
                            entity.IsDeleted = true;
                            entity.DeletionTime = DateTime.Now;
                            entry.State = EntityState.Modified;
                        }
                        break;
                }
            }
        }
    }
}
